﻿using AuApmt.Models.Db.Context;
using AuApmt.Models.DbModels;
using AuApmt.Models.Dto;
using AuApmt.Services.App.HttpAuthInfo;
using AuApmt.Utils;

namespace AuApmt.Repos.Identities
{
    public class UserRepo(
        DataContext context,
        HttpUserInfoService httpUserInfoService
        ) : Repo<User>(context)
    {
        public User? MatchUser(string username, string password)
        {
            var pwdEncrypted = UserPwdEncryption.Encrypt(password);
            return Existing
                .Where(x => x.Name == username && x.Password == pwdEncrypted)
                .FirstOrDefault();
        }

        public List<UserDto> IndexUser()
        {
            return Existing
                .OrderByDescending(x => x.Type)
                .ThenBy(x => x.Name)
                .Select(x => new UserDto
                {
                    Id = x.Id,
                    Name = x.Name,
                    AvatarFileId = x.AvatarFileId,
                    Type = x.Type,
                    Intro = x.Intro,
                    AsClientId = x.AsClientId
                }).ToList();
        }

        public bool CreateUser(
            string? username, string? password, out string? errmsg, bool createAdmin = false)
        {
            username ??= "";
            password ??= "";
            errmsg = CheckModel(username, password);
            if(errmsg is { })
                return false;
            User u = new() { 
                Name = username,
                Password = UserPwdEncryption.Encrypt(password),
                Type = createAdmin ? UserType.Admin : UserType.Staff
            };
            base.Add(u);
            return true;
        }

        public bool UpdateUser(UserDto u, out string? errmsg)
        {
            if(u.Id == 0)
            {
                errmsg = "数据异常";
                return false;
            }
            var current = httpUserInfoService.UserInfo.Value;
            if(current.Id != u.Id && current.Type < UserType.Admin)
            {
                //除管理员之外的用户只能update自己的信息
                errmsg = "无权操作";
                return false;
            }
            errmsg = CheckModel(u.Name, u.Password, u.Id);
            if (errmsg is { })
                return false;
            var user = base.Get(u.Id);
            if(user is null)
            {
                errmsg = "找不到指定用户";
                return false;
            }
            bool wantChangeType = user.Type != u.Type;
            if(wantChangeType && current.Type < UserType.Admin)
            {
                //除管理员之外的用户不能编辑Type
                errmsg = "无权操作";
                return false;
            }
            user.Name = u.Name ?? "";
            user.Intro = u.Intro;
            user.Type = u.Type;
            user.AvatarFileId = u.AvatarFileId;
            user.AsClientId = u.AsClientId;
            if (u.Password is { })
                user.Password = UserPwdEncryption.Encrypt(u.Password);
            base.Update(user, true);
            return true;
        }

        public bool DeleteUser(int id, out string? errmsg)
        {
            var user = base.Get(id);
            if(user is null)
            {
                errmsg = "找不到指定用户";
                return false;
            }
            base.FakeRemove(user, true);
            errmsg = null;
            return true;
        }

        /// <summary>
        /// 检查用户属性是否合法
        /// </summary>
        /// <param name="name">用户名</param>
        /// <param name="password">密码原文</param>
        /// <param name="id">用户id（省略代表正在新建用户）</param>
        /// <returns></returns>
        private string? CheckModel(
            string? name, string? password, int id = 0)
        {
            if (name is null || name.Length < 1 || name.Length > 16)
                return "用户名必须在1-16个字符";
            if (id == 0 || password is { })
            {
                //仅在新建用户或要修改密码时检查
                if (password is null || password.Length < 6 || password.Length > 20)
                    return "密码必须在6-20个字符";
            }
            if (Existing.Any(x => x.Name == name && x.Id != id))
                return "该用户名已经被占用";
            return null;
        }
    }
}
